package icasue.net.socket.thread;

import icasue.net.base.annotations.Nullable;
import icasue.net.protocol.http.common.Const;
import icasue.net.socket.share.MindManger;
import icasue.net.socket.thread.state.SocketState;
import icasue.net.socket.utils.CronSequenceGenerator;
import icasue.net.socket.utils.LogUtil;

import java.net.InetAddress;
import java.net.Socket;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2021/3/26 上午9:50
 * @UpdateDate:
 * @Description: 资源回收线程，
 */
public class ResourceResolver implements Runnable{

    private static final String REPORTER_FORMAT = "\tSocket清理 - Time:[%s] - Identity: [%s] " +
            "\n\t\tLocalTCP: [%s] - RemoteAddress: [%s] - RemoteTCP: [%s] ";
    //每隔5分钟执行一次trigger.
    private String resourceResolveTriggerCorn = icasue.net.socket.common.Const.RESOURCE_RESOLVE_TRIGGER_CORN;
    //FREE态资源释放延时时间(秒), 默认5分钟.
    private Integer resourceResolveDelaySecond = icasue.net.socket.common.Const.RESOURCE_RESOLVE_SECONDS;
    private CronSequenceGenerator cronSequenceGenerator;
    private volatile Long nextTriggerTime;
    private volatile Date nextTrigger;

    private void init(){
        nextTrigger = this.cronSequenceGenerator.next(new Date());
        if(nextTrigger != null){
            nextTriggerTime = nextTrigger.getTime();
        }
    }

    public ResourceResolver(@Nullable String corn, @Nullable Integer delaySecond){
        if(corn != null){
            this.resourceResolveTriggerCorn = corn;
        }
        if(delaySecond != null && delaySecond >= 0){
            this.resourceResolveDelaySecond = delaySecond;
        }
        this.cronSequenceGenerator = new CronSequenceGenerator(this.resourceResolveTriggerCorn);
        init();
    }


    @Override
    public void run() {
        while (nextTriggerTime != null){
            if(nextTriggerTime <= System.currentTimeMillis()){
                try {
                    executeResolve();
                }catch (Throwable e){
                    /*Ignore aly one error to keep clear thread alive!*/
                }
                nextTriggerTime = (nextTrigger = this.cronSequenceGenerator.next(nextTrigger)) == null
                        ? null : nextTrigger.getTime();
            }else {
                // sleep one second
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    private void executeResolve(){
        List<Object> markedClears = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        String TIME = Const.DF_NORMAL.format(now);
        LocalDateTime allowedRangeLeft = now.plusSeconds(-resourceResolveDelaySecond);
        for (Map.Entry<Object, SocketProxy> socketIdentityAndInstance : MindManger.freeSocket.entrySet()) {
            SocketProxy inst = socketIdentityAndInstance.getValue();
            if(inst.getState() != SocketState.FREE){
                continue;
            }
            LocalDateTime beFreeZone = inst.getBeFreeZone();
            if(beFreeZone == null || beFreeZone.isBefore(allowedRangeLeft)){
                Socket socket = inst.getSocket();
                if(socket == null){
                    continue;
                }
                Object[] reportUnit = {
                        TIME,
                        socketIdentityAndInstance.getKey(),
                        socket.getLocalPort(),
                        "UN_CATCH", socket.getPort()
                };
                InetAddress remoteAddress = socket.getInetAddress();
                if(remoteAddress != null){
                    reportUnit[3] = remoteAddress.getHostAddress();
                }
                // Clear Log.
                LogUtil.info(REPORTER_FORMAT, reportUnit);
                // Marked Clear.
                markedClears.add(reportUnit[0]);
                try {
                    socketIdentityAndInstance.getValue().destroy();
                }catch (Throwable e){ /*No Server Exception. */}
            }
        }
        if(markedClears.size() > 0){
            for (Object markedClear : markedClears) {
                MindManger.freeSocket.remove(markedClear);
            }
        }
    }
}
